package com.xiaoyu.lanling.feature.web.view

import `in`.srain.cube.util.CLog
import `in`.srain.cube.util.Env
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Configuration
import android.graphics.Bitmap
import android.net.http.SslError
import android.os.Build
import android.text.TextUtils
import android.util.AttributeSet
import android.view.View
import android.webkit.*
import androidx.annotation.RequiresApi
import com.xiaoyu.base.AppConfig
import com.xiaoyu.base.data.UserData
import java.util.*

class CPWebView : WebView {
    constructor(context: Context) : super(getFixedContext(context)) {
        initBaseSupport()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(getFixedContext(context), attrs) {
        initBaseSupport()
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(getFixedContext(context), attrs, defStyleAttr) {
        initBaseSupport()
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) : super(getFixedContext(context), attrs, defStyleAttr, defStyleRes) {
        initBaseSupport()
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun initBaseSupport() {
        setWebContentsDebuggingEnabled(Env.isDevOrInDevMode())
        webViewClient = webViewClientImplement
        webChromeClient = webChromeClientImplement
        isVerticalScrollBarEnabled = false
        isHorizontalScrollBarEnabled = false
        scrollBarStyle = View.SCROLLBARS_INSIDE_OVERLAY
        isFocusable = true
        isFocusableInTouchMode = true
        settings.allowFileAccess = false
        settings.allowFileAccessFromFileURLs = false
        settings.allowUniversalAccessFromFileURLs = false

        val settings = settings
        settings.javaScriptEnabled = true
        settings.domStorageEnabled = true
        settings.builtInZoomControls = true
        settings.displayZoomControls = false
        settings.useWideViewPort = true
        settings.loadWithOverviewMode = true
        settings.setAppCacheEnabled(true)

        val appCachePath = context.getDir("cache", Context.MODE_PRIVATE).path
        settings.setAppCachePath(appCachePath)
        settings.allowFileAccess = true
        settings.cacheMode = WebSettings.LOAD_DEFAULT
        settings.databaseEnabled = true

        val databasePath = context.getDir("database", Context.MODE_PRIVATE).path
        settings.setGeolocationEnabled(true)
        settings.setGeolocationDatabasePath(databasePath)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            settings.mixedContentMode = WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE
        }

        try {
            removeJavascriptInterface("searchBoxJavaBridge_")
            removeJavascriptInterface("accessibility")
            removeJavascriptInterface("accessibilityTraversal")
        } catch (throwable: Throwable) {
            throwable.printStackTrace()
        }
        syncCookie()
    }

    private fun syncCookie() {
        try {
            val cookieManager = CookieManager.getInstance()
            cookieManager.setAcceptCookie(true)
            cookieManager.removeAllCookie()
            val tokenInfo = UserData.getInstance().tokenInfo
            if (tokenInfo != null && tokenInfo.cookie != null) {
                for (key in tokenInfo.cookieDomains.keys()) {
                    val domain = tokenInfo.cookieDomains.optString(key)
                    val tokenCookie = tokenInfo.cookieName + "=" + tokenInfo.accessToken + ";" +
                            "path=" + tokenInfo.cookiePath + ";" +
                            "domain=" + domain
                    cookieManager.setCookie(domain, tokenCookie)

                    val appKeyCookie = "App-Key=" + AppConfig.appKey() + ";" +
                            "path=" + tokenInfo.cookiePath + ";" +
                            "domain=" + domain
                    CLog.d(TAG, "tokenCookie=%s, appKeyCookie=%s", tokenCookie, appKeyCookie)
                    cookieManager.setCookie(domain, appKeyCookie)
                }
            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                cookieManager.setAcceptThirdPartyCookies(this, true)
                cookieManager.flush()
            } else {
                CookieManager.getInstance().flush()
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun loadUrl(url: String) {
        if (TextUtils.isEmpty(url)) {
            return
        }
        try {
            if (!compatJavaScriptCall(this, url)) {
                super.loadUrl(url)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun loadUrl(url: String, additionalHttpHeaders: Map<String, String>) {
        if (TextUtils.isEmpty(url)) {
            return
        }
        try {
            if (!compatJavaScriptCall(this, url)) {
                super.loadUrl(url, additionalHttpHeaders)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    override fun destroy() {
        try {
            removeAllViews()
            this.webChromeClient = null
            this.webViewClient = null
            super.destroy()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    protected val webViewClientImplement: WebViewClient
        get() = DefaultWebViewClient()

    protected val webChromeClientImplement: WebChromeClient
        get() = DefaultWebChromeClient()

    class DefaultWebViewClient : WebViewClient() {
        override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) {
            super.onReceivedSslError(view, handler, error)
            val vendor = Build.MANUFACTURER
            if (null != vendor) {
                handler.proceed()
            }
        }

        override fun onReceivedError(view: WebView, request: WebResourceRequest, error: WebResourceError) {
            super.onReceivedError(view, request, error)
        }

        override fun onReceivedHttpError(view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse) {
            super.onReceivedHttpError(view, request, errorResponse)
        }

        override fun onPageStarted(view: WebView, url: String, favicon: Bitmap) {
            super.onPageStarted(view, url, favicon)
        }

        override fun onPageFinished(view: WebView, url: String) {
            super.onPageFinished(view, url)
        }
    }

    class DefaultWebChromeClient : WebChromeClient() {
        override fun onJsPrompt(view: WebView, url: String, message: String, defaultValue: String, result: JsPromptResult): Boolean {
            return super.onJsPrompt(view, url, message, defaultValue, result)
        }
    }

    companion object {
        private const val TAG = "CPWebView"

        // TODO: avoid #141132133 issue temporarily, before appcompat updated to 1.2.0
        private fun getFixedContext(context: Context): Context {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
                context.createConfigurationContext(Configuration())
            } else context
        }

        private const val JS_PREFIX = "javascript:"

        fun compatJavaScriptCall(webView: WebView, url: String): Boolean {
            if (!TextUtils.isEmpty(url)) {
                if (url.toLowerCase(Locale.ROOT).startsWith(JS_PREFIX.toLowerCase(Locale.ROOT))) {
                    val js = substringAfter(url, JS_PREFIX)
                    webView.evaluateJavascript(js, null)
                    return true
                }
            }
            return false
        }

        private fun substringAfter(str: String, separator: String): String {
            if (TextUtils.isEmpty(str)) {
                return str
            }

            val pos = str.indexOf(separator)
            return if (pos == -1) {
                ""
            } else str.substring(pos + separator.length)
        }
    }
}